Перейти к основному содержимому

3.06. Справочник по MongoDB

Разработчику Аналитику Тестировщику
Архитектору Инженеру

Справочник по MongoDB

1. Структурные единицы данных

document — документ

Основная единица хранения данных в MongoDB. Представляет собой упорядоченный набор пар ключ–значение. Документы хранятся в BSON, сериализуются в JSON-подобный вид.
Ограничения:

  • Максимальный размер документа — 16 МБ (включая служебную информацию).
  • Документ обязательно должен содержать поле _id.
  • Ключи не могут содержать символы $, ., и не могут начинаться с $. Вложенные ключи не должны содержать . в именах полей (это ограничение BSON, не MongoDB как таковой, но нарушение приведёт к ошибке при вставке).
  • Ключ _id уникален в пределах коллекции.

_id — идентификатор документа

  • Тип: обычно ObjectId, но может быть любого типа, кроме массива и undefined.
  • Значение: генерируется автоматически, если не указано явно.
  • Формат ObjectId (12 байт):
    • 4 байта — временная метка (Unix timestamp)
    • 5 байт — случайный идентификатор машины и процесса
    • 3 байта — счётчик
  • Можно генерировать вручную: new ObjectId(), ObjectId("…"), или передавать строку, число, UUID и т. д.

collection — коллекция

  • Аналог таблицы в реляционных СУБД, но не требует фиксированной схемы.
  • Документы в одной коллекции могут иметь разную структуру.
  • Имена коллекций не должны:
    • быть пустыми
    • содержать символ \0 (null byte)
    • начинаться с system. (зарезервировано для служебных коллекций)
    • содержать $ (в MongoDB 3.6+ разрешено, но не рекомендуется)
  • Коллекции создаются неявно при первой вставке, либо явно через db.createCollection().

database — база данных

  • Группа коллекций.
  • Имена баз данных:
    • не пустые
    • не содержат \0
    • не содержат /, \, ., " и * в MongoDB 4.4+
    • не могут называться admin, local, config (зарезервированы)
  • Ограничение на число баз данных — физическое (место на диске, дескрипторы ОС), логического лимита нет.

2. Формат BSON и его отличия от JSON

Что такое BSON

BSON (Binary JSON) — двоичное представление структурированных данных, расширяющее JSON типами, необходимыми для эффективной работы с данными в памяти и на диске.

Поддерживаемые типы данных (BSON Types, по спецификации):

Код типаНазваниеПример в MongoDB ShellЗамечания
1Double3.14, Number(3.14)IEEE 754 double-precision
2String"текст"UTF-8, обязательны кавычки
3Object{a: 1}вложенный документ
4Array[1, "a", {b: 2}]упорядоченный список
5Binary dataBinData(0, "aGVsbG8=")6 подтипов (generic, function, UUID и др.)
6Undefined (устар.)undefinedне сохраняется в новых версиях; отбрасывается
7ObjectIdObjectId("507f191e810c19729de860ea")12-байтный хеш, генерируется клиентом или сервером
8Booleantrue, false
9Datenew Date("2025-11-21"), ISODate("2025-11-21T00:00:00Z")хранится как 64-битное целое (мс с Unix epoch)
10Nullnull
11Regular Expression/abc/iсохраняется с флагами; не совместим с JS напрямую в индексах
12DBPointer (устар.)не рекомендуется; эквивалентно { $ref: "coll", $id: ObjectId(...) }
13JavaScriptCode("function() { return 1; }")без области видимости (scope)
14Symbol (устар.)Symbol("x")считается устаревшим, эквивалентно строке
15JavaScript with ScopeCode("x + y", { x: 1, y: 2 })код + контекст; редко используется
1632-bit integerNumberInt(42)
17TimestampTimestamp(1700000000, 1)не Date! используется для внутренних операций (репликация); 4 байта — время, 4 — инкремент
1864-bit integerNumberLong("9223372036854775807")строка обязательна при превышении int32
19Decimal128NumberDecimal("3.14159265358979323846264338327950288419716939937510")точная арифметика с плавающей точкой; до 34 цифр
127Min keyMinKey()используется в сортировке/индексах как «минимум всех»
255Max keyMaxKey()используется в сортировке/индексах как «максимум всех»

Важно:

  • При передаче чисел без указания типа (например, 42, 3.14) MongoDB интерпретирует их как Double, если есть дробная часть, иначе — как Int32, если укладывается в 32-битный диапазон (-2^31 … 2^31-1), иначе — Int64.
  • new Date() в shell создаёт Date, а не Timestamp.
  • При сериализации в JSON (например, mongoexport) ObjectId, Date, BinData преобразуются в объекты с полями $oid, $date, $binary.

3. CRUD-операции: методы и их параметры

insertOne(document, options?)

Параметры:

  • document: вставляемый документ (обязательно).
  • options:
    • writeConcern: объект { w: <value>, j: <boolean>, wtimeout: <ms> }
      • w: 0 — без подтверждения; 1 — подтверждение от primary; "majority" — от большинства реплик; <tag> — по тегу replica set.
      • j: true — ждать записи в журнал (journal); false — не ждать (по умолчанию).
      • wtimeout: таймаут подтверждения в мс (по умолчанию — неограничен).
    • bypassDocumentValidation: true/false — пропустить валидацию схемы (если включена).
    • comment: строка — произвольный комментарий (для логов и профилирования).

Возвращает: { acknowledged: true, insertedId: ObjectId(...) }
Ошибки:

  • E11000 duplicate key — при конфликте по _id.
  • DocumentTooLarge — при превышении 16 МБ.

insertMany(documents, options?)

Параметры:

  • documents: массив документов (обязательно; не пустой).
  • options: как у insertOne, плюс:
    • ordered: true (по умолчанию) — вставка прекращается при первой ошибке; false — продолжать, накапливать ошибки.
    • bypassDocumentValidation, writeConcern, comment.

Возвращает:

{
acknowledged: true,
insertedIds: [ ObjectId(...), ObjectId(...), ... ]
}

Если ordered: false и были ошибки — свойство insertedIds содержит _id только успешно вставленных документов; ошибки — в writeErrors.

findOne(filter?, options?)

Параметры:

  • filter: объект запроса (по умолчанию {} — любой документ).
  • options:
    • projection: { поле: 1 | 0 | { $elemMatch: ... } } — какие поля возвращать (1 — включить, 0 — исключить; нельзя смешивать, кроме _id).
    • sort: { поле: 1 | -1 } — сортировка перед выбором.
    • collation: { locale: "ru", strength: 2, caseLevel: true, ... } — правила сравнения строк.
    • hint: название индекса или документ-спецификация индекса — подсказка оптимизатору.
    • comment: строка — для аудита/профилирования.

Возвращает: документ или null.

find(filter?, options?) → курсор

Параметры — такие же, как у findOne, но результат — курсор, а не документ.

Методы курсора (цепочка вызовов):

  • .sort({ поле: 1 | -1 }) — сортировка.
  • .limit(n) — максимум n документов.
  • .skip(n) — пропустить первые n.
  • .project({ ... }) — проекция.
  • .hint({ ... }) — указание индекса.
  • .collation({ ... }) — правила сравнения.
  • .batchSize(n) — размер пакета (по умолчанию ~101 при limit, иначе динамически).
  • .maxTimeMS(ms) — лимит времени выполнения на сервере.
  • .explain("queryPlanner" | "executionStats" | "allPlansExecution") — анализ плана выполнения.
  • .toArray() — получить массив (материализовать).
  • .forEach(fn) — обход (синхронный в shell).
  • .map(fn) — трансформация.
  • .hasNext() / .next() — итерация вручную.

Важно: курсоры ленивые. Запрос выполняется при первом обращении к данным (например, .toArray()).

updateOne(filter, update, options?)

updateMany(filter, update, options?)

Параметры:

  • filter: условие поиска.
  • update: операторы обновления ($set, $inc, $push и т.д.) или замена документа (если без $-операторов — только для updateOne, и только если filter содержит _id).
  • options:
    • upsert: true/false — создать документ, если не найден.
    • writeConcern, bypassDocumentValidation, comment, collation, hint, arrayFilters (для $[<identifier>]).

Операторы обновления (update operators):

ГруппаОператорыКраткое описание
Поле$currentDate, $inc, $min, $max, $mul, $rename, $set, $setOnInsert, $unsetМодификация скалярных полей
Массив$, $[], $[<identifier>], $addToSet, $pop, $pull, $pullAll, $push, $pushAll (устар.), $each, $position, $slice, $sort$push)Работа с массивами
Битовые$bit (and, or, xor)Битовые операции над целыми
Корневой документ$set, $unset, $rename на верхнем уровне

Пример:

db.users.updateOne(
{ _id: ObjectId("...") },
{
$set: { "profile.lastLogin": new Date() },
$inc: { "stats.logins": 1 },
$push: {
"history": {
$each: [{ action: "login", ts: new Date() }],
$position: 0,
$slice: 10
}
}
},
{ upsert: false }
)

deleteOne(filter, options?)

deleteMany(filter, options?)

Параметры:

  • filter — обязательный (даже {} для deleteMany).
  • options:
    • writeConcern, collation, hint, comment.

Возвращает:

{ acknowledged: true, deletedCount: N }

4. Операторы запросов (Query Operators)

MongoDB поддерживает богатый набор операторов для построения фильтров (filter). Все операторы начинаются с $. Их можно комбинировать в одном условии (кроме исключений, оговорённых ниже).

4.1. Операторы сравнения ($eq, $ne, $gt, $gte, $lt, $lte, $in, $nin)

ОператорСинтаксисОписаниеПримечания
$eq{ поле: { $eq: значение } }равноЭквивалентно { поле: значение }, но позволяет избежать конфликтов с зарезервированными именами ($eq, $ne и т.п.)
$ne{ поле: { $ne: значение } }не равноВозвращает документы, где поле отсутствует, не равно значение, или значение null. Не использует индекс для поиска null, если индекс не покрывает null.
$gt{ поле: { $gt: значение } }большеРаботает с числами, датами, строками, ObjectId, BinData (лексикографически).
$gte{ поле: { $gte: значение } }больше или равно
$lt{ поле: { $lt: значение } }меньше
$lte{ поле: { $lte: значение } }меньше или равно
$in{ поле: { $in: [знач1, знач2, …] } }в множествеДо 32 767 элементов (MongoDB 6.0+). Поддерживает регулярные выражения в массиве.
$nin{ поле: { $nin: [знач1, знач2, …] } }не в множествеВозвращает документы, где поле отсутствует или значение не в списке. Не использует индекс эффективно.

Важно:

  • $in и $nin не выполняют поиск null по умолчанию. Чтобы включить null, добавьте его явно: { status: { $in: ["active", "pending", null] } }.
  • При сравнении значений разных типов MongoDB использует порядок типов:
    MinKey < null < numbers < Symbol, String < Object < Array < BinData < ObjectId < Boolean < Date < Timestamp < Regular Expression < MaxKey
    (см. документацию).

4.2. Логические операторы ($and, $or, $not, $nor)

ОператорСинтаксисОписание
$and{ $and: [ {усл1}, {усл2}, … ] }логическое И
$or{ $or: [ {усл1}, {усл2}, … ] }логическое ИЛИ
$not{ поле: { $not: { $gt: 5 } } }отрицание
$nor{ $nor: [ {усл1}, {усл2}, … ] }ни одно из условий не выполняется

4.3. Операторы элементов ($exists, $type, $expr, $jsonSchema, $mod, $regex, $text, $where)

ОператорСинтаксисЗначения параметровПримечания
$exists{ поле: { $exists: true|false } }true — поле присутствует (в т.ч. null); false — отсутствуетНе проверяет тип. Индекс может быть использован.
$type{ поле: { $type: код|строка } }Возможные значения: – 1 / "double"2 / "string"3 / "object"4 / "array"6 / "undefined" (устар.) – 7 / "objectId"8 / "bool"9 / "date"10 / "null"16 / "int"18 / "long"19 / "decimal""number"int, long, double, decimal"binData""regex""javascript""symbol""timestamp""minKey", "maxKey"Можно перечислять массивом: { $type: ["string", "null"] }.
$expr{ $expr: { <выражение агрегации> } }Любое выражение, допустимое в $project и $addFields (например, { $gt: ["$price", { $multiply: ["$qty", 10] }] })Позволяет сравнивать поля внутри документа. Может быть медленным, т.к. не может напрямую использовать индексы (кроме случаев с $eq и покрывающими индексами).
$jsonSchema{ $jsonSchema: { … } }Валидатор по спецификации JSON Schema Draft 4 (частично поддерживаемые ключи: bsonType, required, properties, additionalProperties, pattern, minimum, maximum, minLength, maxLength, enum, dependencies, allOf, anyOf, oneOf, not)Используется в валидации коллекций (db.createCollection(..., { validator: { $jsonSchema: … } })), но также применим в запросах. Не поддерживает $ref, format, default.
$mod{ поле: { $mod: [делитель, остаток] } }делитель > 0, оба целыеРаботает только с целыми (int, long). Не поддерживает double/decimal.
$regex{ поле: { $regex: "паттерн", $options: "i" } }паттерн — строка или объект RegExp; $options: i (регистр), m (многострочный), x (игнор пробелов), s (dotall)Может использовать индекс только при anchored-паттернах (^abc). Регэкспы не могут использовать индекс для $not: { $regex: … }.
$text{ $text: { $search: "ключ", $language: "ru", $caseSensitive: false, $diacriticSensitive: false } }ключ — строка; $language: "danish", "dutch", "english", "finnish", "french", "german", "hungarian", "italian", "norwegian", "portuguese", "romanian", "russian", "spanish", "swedish", "turkish"Требует text-индекса. Игнорирует стоп-слова, стемминг. Возвращает score в поле score, если добавить {$meta: "textScore"} в проекцию.
$where{ $where: "this.price > this.cost" } или функцияJavaScript-код (строка или function() { … })Не рекомендуется: не масштабируется, не использует индексы, подвержен инъекциям, требует security.javascriptEnabled: true. Альтернатива — $expr.

4.4. Операторы массивов ($all, $elemMatch, $size)

ОператорСинтаксисОписание
$all{ поле: { $all: [знач1, знач2] } }поле-массив содержит все указанные значения (порядок не важен). Поддерживает $elemMatch внутри: { tags: { $all: [ { $elemMatch: { category: "a", rating: { $gt: 5 } } } ] } }.
$elemMatch{ поле: { $elemMatch: { подусловие } } }хотя бы один элемент массива удовлетворяет всем условиям в подусловие. Если нужно применить разные условия к разным элементам — используйте $and с несколькими $elemMatch.
$size{ поле: { $size: 3 } }длина массива равна N. Не использует индексы. Альтернатива — хранить длину отдельно (tagsCount) или использовать агрегацию ($size).

Ограничение: $size не поддерживает переменные или выражения. Только константа.

4.5. Геопространственные операторы ($geoWithin, $geoIntersects, $near, $nearSphere, $maxDistance, $minDistance, $geometry, $box, $polygon, $center, $centerSphere)

Для всех гео-операторов требуется геоиндекс: 2dsphere (для WGS84 — долгота/широта) или 2d (для плоских координат 0…360).

ОператорИспользованиеТребования
$geoWithin{ loc: { $geoWithin: { $geometry: … } } }locPoint, LineString, Polygon в GeoJSON. Поддерживает $box, $polygon, $center, $centerSphere (устар., только для 2d).
$geoIntersects{ loc: { $geoIntersects: { $geometry: … } } }Только с 2dsphere, loc и geometry — GeoJSON.
$near / $nearSphere{ loc: { $near: { $geometry: point, $maxDistance: 1000 } } }Возвращает отсортированные по расстоянию документы. $nearSphere учитывает сферичность Земли. Требует одного гео-индекса в коллекции. Не совместим с sort().
$maxDistanceв $near/$nearSphereв метрах (для 2dsphere) или радианах (для 2d).
$minDistanceаналогичноминимальное расстояние (фильтр ближайших).
$geometry{ type: "Point", coordinates: [lon, lat] }Формат GeoJSON. Обязательно: coordinates в порядке [долгота, широта], а не наоборот.
$box{ $box: [[minLon, minLat], [maxLon, maxLat]] }Только для 2d, устаревшее.
$polygon{ $polygon: [[x1,y1], [x2,y2], …] }Только для 2d.
$center / $centerSphere{ $center: [[centerLon, centerLat], radius] }radius — в градусах (для $center) или радианах (для $centerSphere). Только 2d.

5. Проекция (projection)

Указывает, какие поля включать/исключать в результатах.

Синтаксис:

{ <поле1>: <0|1|выражение>, <поле2>:}

Правила:

  • Нельзя смешивать 0 и 1, кроме исключения: _id: 0 при общем 1.
  • _id включается по умолчанию. Чтобы исключить — указать _id: 0.
  • 1 — включить поле; 0 — исключить.
  • Можно использовать $slice, $elemMatch, $ (позиционный оператор) на уровне массивов.
  • В $find() проекция не поддерживает агрегационные выражения ($add, $concat и т.д.). Для этого — $project в агрегации.

Примеры:

// Только name и email, без _id
{ name: 1, email: 1, _id: 0 }

// Все поля, кроме password и tokens
{ password: 0, tokens: 0 }

// Первые 3 элемента массива history
{ history: { $slice: 3 } }

// Последние 2 элемента
{ history: { $slice: -2 } }

// Элементы 5–10 (пропустить 5, взять 5)
{ history: { $slice: [5, 5] } }

// Только первый элемент массива, удовлетворяющий условию
{ "comments": { $elemMatch: { rating: { $gte: 4 } } } }

// Позиционный оператор: вернуть только тот элемент comments, который matched в запросе
db.posts.find(
{ "comments.author": "timur" },
{ "comments.$": 1 } // → вернёт только первый подходящий комментарий
)

Важно:

  • $elemMatch в проекции возвращает только первый подходящий элемент.
  • $ работает только если в фильтре используется условие на элемент массива (например, "comments.author"), и возвращает первый matched элемент.

6. Сортировка (sort)

Синтаксис:

{ поле1: 1 | -1, поле2: 1 | -1,}
  • 1 — по возрастанию (A→Z, 0→∞, старые → новые даты)
  • -1 — по убыванию

Поведение:

  • Сортирует после фильтрации.
  • Поддерживает составные сортировки.
  • Может использовать составной индекс, если порядок полей и направления совпадают.
  • null, undefined, отсутствующие поля считаются равными и идут в конце при 1, в начале при -1.
  • MinKey всегда первым при 1, MaxKey — последним.
  • При сортировке строк применяется collation (если указана), иначе — бинарное сравнение (UTF-8 byte order).

Ограничения:

  • Без индекса сортирует в памяти. Лимит — 32 MB (иначе ошибка Sort exceeded memory limit).
  • Чтобы обойти — использовать allowDiskUse: true в агрегации, либо добавить индекс.
  • Нельзя сортировать по полю, не входящему в состав индекса, если индекс — multikey (массив), и сортируемое поле не последнее в индексе.

Collation в сортировке:

db.coll.find().sort({ name: 1 }).collation({
locale: "ru",
strength: 2, // 1 — только базовые символы, 2 — регистронезависимо, 3 — чувствительно к регистру
caseLevel: false, // учитывать регистр отдельно (для strength=1 и 2)
numericOrdering: true, // "2" < "10" (вместо лексикографического "10" < "2")
caseFirst: "upper", // при strength=3: upper/lower/off
backwards: false // для некоторых языков (французский) — акценты в обратном порядке
})

7. Индексы (Indexes)

Типы индексов:

ТипСозданиеОписание
Single Fielddb.coll.createIndex({ поле: 1 })По одному полю. Порядок (1/-1) важен для сортировки и составных индексов, но не для поиска равенства.
Compounddb.coll.createIndex({ a: 1, b: -1, c: 1 })По нескольким полям. Правило EON: Equality → Sort → Range. Пример: { status: 1, createdAt: -1, score: 1 } эффективен для status = "active", sort(createdAt), score > 0.5.
MultikeyавтоматическиПри индексации поля-массива. Создаёт отдельную запись в индексе для каждого элемента массива. Ограничения: не более одного multikey-поля в составном индексе; нельзя индексировать массив объектов, если запрос использует $elemMatch с несколькими условиями на разные подполя (требуется partialFilterExpression).
Textdb.coll.createIndex({ title: "text", body: "text" }, { default_language: "russian" })Полноценный текстовый поиск. Поддерживает веса: { title: "text", body: "text" }, { weights: { title: 10, body: 1 } }. Один text-индекс на коллекцию (можно объединять поля).
Wildcarddb.coll.createIndex({ "$**": 1 }) или { "user.$**": 1 }Индексирует все вложенные поля. Полезен для неизвестной/динамической схемы. Поддерживает исключения: { "user.$**": 1 }, { "user.password": 0 }. Не поддерживает TTL, уникальность, partial.
2dspheredb.coll.createIndex({ loc: "2dsphere" })Для геоданных в формате GeoJSON (Point, LineString, Polygon). Поддерживает $near, $geoWithin, $geoIntersects.
2ddb.coll.createIndex({ loc: "2d" })Для плоских координат [x, y], x,y ∈ [−180, 180]. Устаревший.
Hasheddb.coll.createIndex({ _id: "hashed" })Хэш-индекс по одному полю. Используется только для шардирования по хэшу. Не поддерживает диапазонные запросы ($gt, sort).
TTLdb.coll.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 })Автоматическое удаление документов по времени жизни. Поле должно быть Date. Работает только на верхнеуровневом поле. Удаление — фоновым демоном (каждые 60 сек).

Опции при создании индекса:

ОпцияЗначение по умолчаниюОписание
uniquefalseЗапрещает дубликаты значений (включая null). Если поле отсутствует — считается как null.
partialFilterExpressionnullУсловие ({ status: "active" }), по которому документы попадают в индекс. Экономит место и повышает производительность.
sparsefalseУстаревшая альтернатива partialFilterExpression. Индексирует только документы, где поле присутствует (но не null). Не рекомендуется.
backgroundfalseСоздаёт индекс в фоне без блокировки записи. Влияет только на создание, не на использование. В MongoDB 4.2+ все индексы фоновые по умолчанию в replica set.
collationлокаль по умолчаниюПравила сравнения строк. Индекс с collation может использоваться только в запросах с той же collation.
nameавтогенерируемоеИмя индекса (для удаления: db.coll.dropIndex("имя")).
hiddenfalse (MongoDB 4.4+)Скрывает индекс от планировщика запросов. Полезно для экспериментов.
expireAfterSecondsТолько для TTL-индексов.
weights{}Только для text-индексов.
default_language"english"Для text-индексов. Поддерживает "russian".
language_override"language"Поле в документе, переопределяющее язык для text-индекса.

Ограничения индексов:

  • Максимум 64 индекса на коллекцию (включая _id).
  • Общий размер всех индексов — ограничен памятью/диском, но рекомендуется не превышать 50% RAM.
  • Составной индекс: максимум 32 поля.
  • Размер ключа индекса — не более 1024 байт (иначе KeyTooLarge).
  • _id-индекс всегда уникален и не может быть удалён.

Управление индексами:

// Создать
db.coll.createIndex({ a: 1 }, { name: "idx_a", unique: true })

// Посмотреть все
db.coll.getIndexes()

// Удалить по имени
db.coll.dropIndex("idx_a")

// Удалить по спецификации
db.coll.dropIndex({ a: 1 })

// Удалить все (кроме _id)
db.coll.dropIndexes()

// Статистика по использованию (требует serverStatus или профилирование)
db.coll.aggregate([{ $indexStats: {} }])

8. Агрегация (Aggregation Pipeline)

Агрегация — мощный механизм трансформации, фильтрации, группировки и анализа данных. Pipeline состоит из стадий (stages), каждая из которых преобразует поток документов и передаёт результат следующей.

Основные характеристики:

  • Выполняется последовательно, слева направо.
  • Каждая стадия получает поток документов (не массив), обрабатывает их по одному (за исключением $group, $sort, $bucket и т.п.).
  • Промежуточные результаты могут материализовываться на диске (если allowDiskUse: true и объём > 100 МБ).
  • Поддерживает оптимизации: свёртку $project/$addFields, перенос $match в начало, слияние $sort + $limit, pushdown в шарды.
  • Все стадии начинаются с $.

8.1. Стадии Pipeline (в алфавитном порядке)

$addFields

Добавляет новые поля или перезаписывает существующие (аналог $set в новых версиях).

Синтаксис:

{ $addFields: { <новоеПоле>: <выражение>,} }

Особенности:

  • Не удаляет исходные поля.
  • Можно ссылаться на уже добавленные поля внутри одного объекта только в MongoDB 5.0+ (до этого — ошибка циклической зависимости).
  • Пример:
    { $addFields: {
    total: { $add: ["$price", "$tax"] },
    isExpensive: { $gt: ["$price", 1000] },
    tagsCount: { $size: "$tags" }
    } }
$bucket

Группирует входные документы по диапазонам («ведрам») на основе значения числового или строкового поля.

Синтаксис:

{
$bucket: {
groupBy: <выражение>,
boundaries: [<min>, <val1>, <val2>,, <max>],
default: <значение>,
output: { <поле>: { <аккум>: <выражение> },}
}
}

Требования:

  • boundaries — массив возрастающих значений (числа, даты, строки).
  • Каждый документ попадает в ведро [boundaries[i], boundaries[i+1]), т.е. левая граница включена, правая — нет.
  • default — значение для документов вне всех границ (например, "other"). Обязательно, если возможны такие документы.

Пример:

{
$bucket: {
groupBy: "$score",
boundaries: [0, 20, 40, 60, 80, 100],
default: "invalid",
output: {
count: { $sum: 1 },
avgScore: { $avg: "$score" }
}
}
}
// → { _id: 0, count: 12, avgScore: 15.2 }, { _id: 20, … }, { _id: "invalid", … }
$bucketAuto

Автоматически определяет границы вёдер для равномерного распределения документов.

Синтаксис:

{
$bucketAuto: {
groupBy: <выражение>,
buckets: <N>,
output: {},
granularity: "R5" | "R10" | "E6" | "E12" | "POWERSOF2" | "1-2-5" | "E192" | "E48" | "E24" | "E3"
}
}

Параметры:

  • buckets: целое число ≥ 1.
  • granularity: округление границ (стандарты IEC/ISO для электроники и инженерии). Например, "1-2-5" даёт границы вида 10, 20, 50, 100, 200….

Примечание: не гарантирует точное равенство числа документов в ведре — зависит от распределения данных.

$collStats

Возвращает статистику по коллекции (размер, индексы, операции).

Синтаксис:

{ $collStats: {
latencyStats: { histograms: true },
storageStats: { scale: 1 },
count: { },
queryExecStats: { }
} }

Возможные подразделы:

  • latencyStats — задержки операций (чтение, запись, команды).
  • storageStats — размер данных, индексов, фрагментация (аналог db.coll.stats()).
  • count — общее число документов.
  • queryExecStats — статистика по планам выполнения запросов.

Используется для мониторинга и диагностики. Результат — один документ.

$count

Считает общее число документов на входе и выводит один документ с полем.

Синтаксис:

{ $count: "fieldName" }

Результат: { fieldName: 42 }
Эквивалент:

{ $group: { _id: null, fieldName: { $sum: 1 } } },
{ $project: { _id: 0, fieldName: 1 } }
$densify

(Начиная с MongoDB 5.1)
Добавляет «пропущенные» документы в последовательность на основе временной или числовой оси.

Синтаксис:

{
$densify: {
field: "timestamp",
range: {
step: 3600, // шаг в секундах (для Date), или число
unit: "hour", // "millisecond", "second", "minute", "hour", "day", "week", "month", "quarter", "year"
bounds: [start, end] | "full" // "full" — от min до max по данным
}
}
}

Пример:

// Заполнить пропуски в почасовых метриках
{ $densify: { field: "ts", range: { step: 1, unit: "hour", bounds: "full" } } }

Требует, чтобы field был Date или числом. Часто комбинируется с $fill.

$documents

(Внутренняя стадия, начиная с MongoDB 5.1)
Позволяет вставить документы непосредственно в pipeline, без обращения к коллекции.

Синтаксис:

{ $documents: [ { a: 1 }, { b: 2 } ] }

Использование:

  • В $facet, $lookup (sub-pipeline).
  • Для тестирования, генерации данных, join с «виртуальной» таблицей.

Аналог VALUES в SQL.

$facet

Параллельное выполнение нескольких подконвейеров над одним и тем же набором документов.

Синтаксис:

{
$facet: {
output1: [ <стадии> ],
output2: [ <стадии> ],

}
}

Результат: один документ с полями output1, output2, … — каждый содержит массив результатов соответствующего подконвейера.

Пример: мульти-агрегация для дашборда:

{
$facet: {
byStatus: [
{ $group: { _id: "$status", count: { $sum: 1 } } }
],
topUsers: [
{ $group: { _id: "$user", total: { $sum: "$amount" } } },
{ $sort: { total: -1 } },
{ $limit: 5 }
],
stats: [
{ $group: {
_id: null,
avg: { $avg: "$amount" },
max: { $max: "$amount" },
min: { $min: "$amount" }
} }
]
}
}

Ограничения:

  • Каждый подконвейер должен умещаться в 16 МБ (результат массива).
  • Нельзя использовать $out, $merge, $search, $geoNear внутри $facet.
$fill

(Начиная с MongoDB 5.3)
Заполняет null/отсутствующие значения в полях на основе соседних документов (аналог fillna в pandas).

Синтаксис:

{
$fill: {
partitionBy: "$userId",
sortBy: { ts: 1 },
output: {
temperature: { value: 0 },
status: { method: "locf" }, // locf — last observation carried forward
location: { method: "linear" } // только для чисел/дат
}
}
}

Методы:

  • value: подставить константу.
  • locf: взять значение из предыдущего документа.
  • linear: линейная интерполяция (только числа/даты; требует равномерного шага в sortBy).

Часто используется после $densify.

$geoNear

Возвращает документы, отсортированные по расстоянию до заданной точки.

Синтаксис:

{
$geoNear: {
near: { type: "Point", coordinates: [lon, lat] },
distanceField: "dist", // обязательное поле для записи расстояния
distanceMultiplier: 1, // коэффициент (например, для км: 0.001 при метрах)
maxDistance: 5000, // в метрах (для 2dsphere)
minDistance: 0,
query: { status: "active" }, // дополнительный фильтр
spherical: true, // для 2dsphere — true
key: "loc", // имя поля с геоданными (если не "_id")
includeLocs: "coords" // сохранить координаты найденной точки
}
}

Особенности:

  • Должна быть первой стадией в pipeline.
  • Требует одного геоиндекса (2dsphere или 2d).
  • Возвращает отсортированные документы — $sort после $geoNear избыточен.
$graphLookup

Рекурсивный поиск по графу (например, иерархия подразделений, цепочка рефералов).

Синтаксис:

{
$graphLookup: {
from: "employees",
startWith: "$_id",
connectFromField: "_id",
connectToField: "managerId",
as: "subordinates",
maxDepth: 5,
depthField: "level",
restrictSearchWithMatch: { status: "active" }
}
}

Параметры:

  • from — имя коллекции (только локальной БД).
  • startWith — начальное значение (массив или скаляр).
  • connectFromField / connectToField — поля для связи («откуда» → «куда»).
  • as — имя выходного массива.
  • maxDepth — глубина рекурсии (по умолчанию — бесконечно, но ограничена сервером).
  • depthField — добавить поле с уровнем вложенности в каждый найденный документ.
  • restrictSearchWithMatch — фильтр на каждый шаг рекурсии.

Важно: неэффективен на больших графах; лучше использовать Neo4j для сложных графовых задач.

$group

Группирует документы по выражению и вычисляет агрегаты.

Синтаксис:

{
$group: {
_id: <выражение>, // null — одна группа; "$field" — по полю; { a: "$a", b: "$b" } — составной ключ
поле1: { <аккум>: <выражение> },

}
}

Аккумуляторы (см. раздел 8.2 ниже):
$sum, $avg, $min, $max, $first, $last, $push, $addToSet, $stdDevPop, $stdDevSamp, $mergeObjects, $accumulator (пользовательский), $function (JS), $top, $bottom, $topN, $bottomN (MongoDB 5.2+).

Пример:

{
$group: {
_id: { year: { $year: "$createdAt" }, month: { $month: "$createdAt" } },
totalSales: { $sum: "$amount" },
uniqueUsers: { $addToSet: "$userId" },
firstOrder: { $first: "$$ROOT" }
}
}

Оптимизация: если _id: null, MongoDB может использовать оптимизацию scalar group (без перегруппировки).

$indexStats

Возвращает статистику по использованию индексов.

Синтаксис:

{ $indexStats: {} }

Результат — документы вида:

{
name: "status_1_createdAt_-1",
key: { status: 1, createdAt: -1 },
host: "host:port",
accesses: { ops: 1250, since: ISODate("…") }
}

Поле ops — число операций с момента since. Сбрасывается при рестарте сервера.

$limit

Ограничивает количество документов на выходе.

Синтаксис:

{ $limit: 10 }

Оптимизации:

  • Если перед $limit есть $sort, MongoDB может использовать Top-K Sort (не сортировать всю коллекцию, а держать только K лучших).
  • Может «проталкиваться» в шарды.
$listLocalSessions, $listSessions

(Для внутреннего использования)
Возвращают активные сессии на локальном узле или в кластере.

$lookup

Выполняет left outer join с другой коллекцией.

3 формы:

  1. Обычная (равенство полей):

    {
    $lookup: {
    from: "orders",
    localField: "_id",
    foreignField: "userId",
    as: "orders"
    }
    }
  2. Sub-pipeline (гибкий join):

    {
    $lookup: {
    from: "orders",
    let: { userId: "$_id", status: "$premium" },
    pipeline: [
    { $match: {
    $expr: {
    $and: [
    { $eq: ["$userId", "$$userId"] },
    { $gte: ["$amount", { $cond: ["$$status", 1000, 500] }] }
    ]
    }
    }
    },
    { $project: { _id: 0, date: 1, amount: 1 } }
    ],
    as: "bigOrders"
    }
    }
  3. Uncorrelated subquery (MongoDB 5.1+) — без let, статический pipeline.

Ограничения:

  • from — только локальная БД, не шардированная (если сама коллекция не шардирована).
  • Результат — массив (даже если 0 или 1 документ).
  • Для join с шардами требуется, чтобы localField был shard key.
$match

Фильтрует документы по условию (аналог find()).

Синтаксис:

{ $match: { status: "active", score: { $gt: 10 } } }

Оптимизация:

  • MongoDB «перемещает» $match как можно ближе к началу pipeline.
  • Если стоит сразу после $lookup, может использоваться для pushdown в from-коллекцию.
$merge

Записывает результат pipeline в коллекцию с объединением (в отличие от $out, который заменяет коллекцию).

Синтаксис:

{
$merge: {
into: "report_daily",
on: "_id", // ключ объединения (массив полей)
let: { ts: "$timestamp" },
whenMatched: [ // pipeline для обновления существующих
{ $addFields: { updatedAt: "$$ts" } },
{ $mergeObjects: ["$$new", "$$ROOT"] }
],
whenNotMatched: "insert", // "insert", "discard", "fail"
unique: true // требует уникального индекса по `on`
}
}

Режимы whenMatched:

  • "replace" — полная замена (по умолчанию).
  • "keepExisting" — оставить старый документ.
  • "merge" — объединить поля (новые перезаписывают старые).
  • "fail" — ошибка при конфликте.
  • [ pipeline ] — кастомная логика (можно использовать $$new, $$ROOT, $$let).

Поддерживает шардированные коллекции (начиная с 4.2). Идеально для ETL, материализованных представлений.

$out

Записывает результат pipeline в коллекцию, заменяя её содержимое.

Синтаксис:

{ $out: "new_collection_name" }
// или
{ $out: { db: "otherdb", coll: "target" } } // 4.4+

Ограничения:

  • Коллекция создаётся, если не существует.
  • Всегда атомарна (старая коллекция удаляется только после успешного завершения).
  • Не поддерживает шардирование.
  • Нельзя использовать после $facet, $geoNear, $collStats.
$planCacheStats

Возвращает статистику по кэшу планов запросов.

Результат — документы с queryHash, planCacheKey, works, fails, createdFromQuery, createdFromHint.

$project

Выбирает/переименовывает/вычисляет поля (устаревшая форма $addFields + проекция).

Синтаксис:

{ $project: {
_id: 0,
name: 1,
email: "$contact.email",
year: { $year: "$createdAt" },
tagsCount: { $size: "$tags" }
} }

В новых версиях предпочтительно использовать $addFields + $unset, т.к. $project не позволяет одновременно включать и вычислять поля без дублирования.

$redact

(Устаревший, начиная с 4.4)
Рекурсивно фильтрует документы и поддокументы на основе выражения.

Замена: $project + $cond + $filter.

$replaceRoot, $replaceWith

Поднимает вложенный документ на верхний уровень.

Синтаксис:

{ $replaceRoot: { newRoot: "$embedded.doc" } }
// или
{ $replaceWith: "$embedded.doc" } // MongoDB 4.2+

Пример:

// Было: { _id: 1, data: { name: "Timur", age: 30 } }
// После $replaceWith: "$data" → { name: "Timur", age: 30 }
$sample

Случайная выборка документов.

Синтаксис:

{ $sample: { size: 100 } }

Особенности:

  • Не гарантирует равномерность при size > 5% коллекции.
  • При size ≥ collection size — возвращает все документы в случайном порядке.
  • Не использует индексы.
$search (MongoDB Atlas)

Полнотекстовый, семантический, векторный поиск в Atlas Search.

Пример:

{
$search: {
index: "default",
text: {
query: "mongodb",
path: ["title", "body"]
}
}
}

Требует Atlas. Не доступен в Community/Enterprise Edition без Atlas.

$set

Псевдоним для $addFields (начиная с MongoDB 4.2). Предпочтителен для читаемости.

$setWindowFields

(Начиная с MongoDB 5.0)
Аналог оконных функций в SQL (OVER, PARTITION BY, ORDER BY, ROWS/RANGE).

Синтаксис:

{
$setWindowFields: {
partitionBy: "$department",
sortBy: { salary: -1 },
output: {
rank: { $rank: {} },
movingAvg: {
$derivative: { // или $avg, $sum, $min, $max, $stdDevPop, $stdDevSamp
input: "$salary",
unit: "month"
},
window: { documents: [-2, 0] } // текущий + 2 предыдущих
},
cumulativeCount: {
$documentNumber: {},
window: { documents: ["unbounded", "current"] }
}
}
}
}

Встроенные функции:

  • $rank, $denseRank, $documentNumber
  • $shift — сдвиг значений
  • $derivative, $integral — для временных рядов
  • $avg, $sum, $min, $max, $stdDevPop, $stdDevSamp — с окном

Требует сортировки (sortBy). Мощный инструмент для аналитики временных рядов и ранжирования.

$skip

Пропускает N документов.

Синтаксис:

{ $skip: 20 }

Неэффективен без $sort + индекса. Лучше использовать ключевое постраничное разбиение (_id > lastSeenId).

$sort

Сортирует документы.

Синтаксис:

{ $sort: { name: 1, createdAt: -1 } }

Оптимизации:

  • Может использовать индекс.
  • $sort + $limitTop-K.
  • В MongoDB 6.0+ поддерживает сортировку по выражению: { $sort: { fullName: { $concat: ["$firstName", " ", "$lastName"] } } }.
$sortByCount

Группирует по полю и сортирует по убыванию количества.

Синтаксис:

{ $sortByCount: "$status" }

Эквивалент:

{ $group: { _id: "$status", count: { $sum: 1 } } },
{ $sort: { count: -1 } }
$unionWith

Объединяет результаты двух pipeline’ов (аналог UNION ALL в SQL).

Синтаксис:

{ $unionWith: "other_collection" }
// или
{ $unionWith: {
coll: "logs_old",
pipeline: [
{ $match: { ts: { $lt: ISODate("2025-01-01") } } },
{ $addFields: { source: "archive" } }
]
} }

Особенности:

  • Порядок не гарантируется — добавьте $sort при необходимости.
  • Коллекции должны быть в одной БД.
$unset

Удаляет указанные поля.

Синтаксис:

{ $unset: ["password", "tokens", "internal.flag"] }

Эквивалентно $project: { password: 0, tokens: 0, … }, но короче и безопаснее (игнорирует отсутствующие поля).

$unwind

Разворачивает массив в отдельные документы.

Синтаксис:

{ $unwind: "$tags" }
// или
{ $unwind: {
path: "$comments",
includeArrayIndex: "idx",
preserveNullAndEmptyArrays: true
} }

Параметры:

  • includeArrayIndex — добавить поле с индексом элемента.
  • preserveNullAndEmptyArrays — если true, документы с null/[] не удаляются.

После $unwind документы дублируются — учитывайте при агрегации ($group$sum: 1 даст число элементов, а не документов!).


8.2. Аккумуляторы (в $group, $setWindowFields, $bucket)

АккумуляторОписаниеПример
$sumсумма/счётчик{ $sum: 1 }, { $sum: "$amount" }
$avgсреднее{ $avg: "$score" }
$min / $maxминимум/максимум{ $min: "$price" }
$first / $lastпервый/последний в группе{ $first: "$ts" }
$pushсобирает все значения в массив{ $push: "$tag" }
$addToSetсобирает уникальные значения{ $addToSet: "$userId" }
$stdDevPop / $stdDevSampстандартное отклонение (генеральная/выборочная){ $stdDevSamp: "$value" }
$mergeObjectsобъединяет объекты (последний перезаписывает){ $mergeObjects: "$config" }
$accumulatorпользовательская агрегатная функция (на JS/C++)
$functionпользовательская скалярная функция (на JS)
$top / $bottomвозвращает документ с экстремальным значением{ $top: { output: "$$ROOT", sortBy: { score: -1 } } }
$topN / $bottomNвозвращает N документов{ $topN: { n: 3, output: "$$ROOT", sortBy: { ts: -1 } } }

$top/$bottom — MongoDB 5.2+, заменяют конструкции с $push + $slice + $arrayElemAt.


9. Транзакции (Transactions)

Начиная с MongoDB 4.0 (реплика-сеты) и 4.2 (шардированные кластеры), MongoDB поддерживает много-документные, много-коллекционные, много-БД транзакции с уровнем изоляции snapshot (MVCC).

Основные характеристики:

  • Атомарность, согласованность, изолированность (snapshot), но не долговечность по умолчанию (см. writeConcern).
  • Максимальная длительность: 60 секунд (по умолчанию), можно увеличить до 120 секунд через transactionLifetimeLimitSeconds.
  • Максимум 1 000 изменённых документов за одну транзакцию (ограничение на лог операций).
  • Все операции внутри транзакции должны использовать один сессион (session).
  • Поддерживаются: insert, update, delete, findAndModify, aggregate (без $out, $merge, $lookup на шардированные коллекции), distinct.

Работа с транзакциями (в shell / драйверах):

// 1. Создать сессию
const session = db.getMongo().startSession();

// 2. Начать транзакцию
session.startTransaction({
readConcern: { level: "snapshot" },
writeConcern: { w: "majority", j: true },
readPreference: "primary" // обязательно для транзакций
});

try {
// 3. Выполнять операции в сессии
db.users.updateOne(
{ _id: ObjectId("..."), balance: { $gte: 100 } },
{ $inc: { balance: -100 } },
{ session: session }
);

db.orders.insertOne(
{ userId: ObjectId("..."), amount: 100, ts: new Date() },
{ session: session }
);

// 4. Зафиксировать
session.commitTransaction();
print("Транзакция успешна");

} catch (error) {
// 5. Откат при ошибке
session.abortTransaction();
throw error;

} finally {
// 6. Закрыть сессию (важно!)
session.endSession();
}

Параметры startTransaction():

ПараметрВозможные значенияОписание
readConcern{ level: "local" | "majority" | "snapshot" }snapshot — единственный допустимый уровень в транзакциях (гарантирует согласованное чтение).
writeConcern{ w: <n | "majority" | tag>, j: true|false, wtimeout: ms }Должен быть одинаковым для всех операций в транзакции. Если не указан — берётся из настроек коллекции (но лучше указать явно).
maxCommitTimeMSчисло мсТаймаут на фазу коммита (по умолчанию — 10 мин).

Ограничения и особенности:

  • Нельзя использовать:
    • $out, $merge, $search, $geoNear, $indexStats, $collStats, $listSessions, $currentOp, $planCacheStats.
    • Операции DDL: createCollection, drop, createIndex, dropIndex, renameCollection.
  • Нельзя читать из одной коллекции и писать в неё же в рамках одной транзакции (write skew — разрешено с MongoDB 4.2+, но с оговорками).
  • При перегрузке оперативной памяти транзакция может быть прервана с ошибкой TransientTransactionError.
  • Транзакции не видны в mongostat, serverStatus — только в currentOp.

Обработка ошибок:

  • TransientTransactionError — повторить всю транзакцию (например, при failover).
  • UnknownTransactionCommitResult — неизвестен результат коммита (повторить commit, но не всю транзакцию).
  • WriteConflict — конфликт записи (автоматически перезапускается до 120 сек; если не удалось — ошибка).

Рекомендуется оборачивать транзакции в retry-логику (например, с retryWrites: true + withTransaction() в драйверах).


10. Репликация (Replica Sets)

Группа серверов (обычно 3+), обеспечивающая высокую доступность и отказоустойчивость.

Архитектура:

  • Primary — один, принимает все записи и большинство чтений.
  • Secondary — 0+, реплицируют данные, обслуживают чтение (если readPreference разрешает).
  • Arbiter — «арбитр», без данных, участвует только в выборах (не рекомендуется в продакшене).
  • Hidden, Delayed, Priority 0 — специальные роли вторичных узлов.

Конфигурация replica set (в shell):

rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "node1:27017" },
{ _id: 1, host: "node2:27017", priority: 0.5 },
{ _id: 2, host: "node3:27017", hidden: true, votes: 0 }
]
})

Важные параметры узла:

ПараметрТипЗначение по умолчаниюОписание
priorityчисло1.0Приоритет при выборах. 0 — не может стать primary.
votes0 | 11Голосует ли узел. Максимум 7 голосующих узлов.
hiddenboolfalseНе виден клиентам (только для бэкапов, аналитики).
slaveDelayсек0Задержка репликации (только для hidden: true). Для защиты от drop.
tagsобъект{}Произвольные метки (например, { dc: "ufa", rack: "a1" }) — используются в readPreference и writeConcern.
buildIndexesbooltrueСтроить ли индексы (если false — нельзя сделать primary).

Write Concern в replica set:

{ w: 1 }           // подтверждение от primary  
{ w: 2 } // от primary + 1 secondary
{ w: "majority" } // от большинства голосующих узлов
{ w: "tag:dc_ufa" } // от узлов с тегом `dc: "ufa"`
{ j: true } // ждать записи в journal
{ wtimeout: 5000 } // таймаут в мс

Read Preference:

РежимОписание
primaryтолько primary (по умолчанию)
primaryPreferredprimary, при недоступности — secondary
secondaryтолько secondary
secondaryPreferredsecondary, при недоступности — primary
nearestближайший по latency (не по гео!)

Можно комбинировать с тегами:

db.collection.find().readPref("secondary", [ { dc: "ufa" } ])

Операции управления:

rs.status()          // состояние replica set  
rs.conf() // конфигурация
rs.reconfig(conf) // изменить конфиг (без downtime при force: false)
rs.stepDown(60) // primary уступает за 60 сек
rs.freeze(120) // запретить участвовать в выборах 120 сек
rs.syncFrom("host") // принудительно реплицировать с узла (только для secondary)

Ограничения:

  • Минимум 3 узла для отказоустойчивости (2 + arbiter не рекомендуется — потеря данных при split-brain).
  • Все узлы должны иметь одинаковую версию MongoDB (мажорная и минорная).
  • Максимум 50 узлов в replica set, из них 7 голосующих.

11. Шардирование (Sharding)

Горизонтальное масштабирование: данные распределяются по нескольким replica sets (шардам).

Компоненты кластера:

  • Config Server — replica set, хранит метаданные (маршруты, диапазоны шардов).
  • mongos — роутер (stateless), принимает клиентские запросы, перенаправляет в шарды.
  • Shard — replica set, хранит данные (обычно 2+ шарда + 1 arbiter на каждый).

Типы шардирования:

  1. Hashed Shard Key
    • Ключ: { userId: "hashed" }
    • Равномерное распределение, но не поддерживает диапазонные запросы.
  2. Range-based Shard Key
    • Ключ: { region: 1, createdAt: -1 }
    • Поддерживает диапазоны и сортировку, но риск «горячего» шарда при монотонном ключе (например, ObjectId).
  3. Zone Sharding (до 5.0 — Tag-Aware Sharding)
    • Привязка диапазонов к зонам: { region: "EU" } → shard01, { region: "RU" } → shard02.
  4. Custom Shard Key (MongoDB 4.4+)
    • Композитный ключ с хэшированием части: { region: 1, userId: "hashed" }.

Включение шардирования:

// 1. Включить шардирование для БД
sh.enableSharding("universe")

// 2. Определить shard key и включить для коллекции
sh.shardCollection("universe.posts", { "authorId": 1, "_id": -1 }, false, { unique: true })

Операции:

sh.status()                  // состояние кластера  
sh.splitAt("universe.posts", { authorId: "user123" })
sh.splitFind("universe.posts", { authorId: { $gt: "user500" } })
sh.moveChunk("universe.posts", { authorId: "user123" }, "shard02")
sh.disableBalancing("universe.posts")
sh.enableBalancing("universe.posts")

Важные параметры:

  • chunkSize — размер чанка (по умолчанию 64 МБ). Можно изменить глобально: db.settings.updateOne({ _id: "chunksize" }, { $set: { value: 128 } }).
  • balancer — фоновый процесс перераспределения чанков. Управляется через sh.setBalancerState(true/false).
  • zone — логическая группа шардов. Пример:
    sh.addShardToZone("shard01", "RU")
    sh.addShardToZone("shard02", "EU")
    sh.updateZoneKeyRange("universe.users", { region: "RU" }, { region: "RU", userId: MaxKey }, "RU")

Ограничения:

  • Shard key не может быть изменён после шардирования коллекции.
  • Коллекция должна иметь уникальный индекс, покрывающий shard key (если unique: true).
  • _id — всегда индексирован, но не обязательно является частью shard key.
  • _id должен быть уникальным глобально (все шарды).

12. Безопасность

Аутентификация

  • SCRAM-SHA-1 (устар.), SCRAM-SHA-256 (по умолчанию), x.509, LDAP, Kerberos (Enterprise).
  • Пользователи создаются в БД admin или локальной БД.
use admin
db.createUser({
user: "timur",
pwd: passwordPrompt(), // безопасный ввод
roles: [
{ role: "readWrite", db: "universe" },
{ role: "dbAdmin", db: "universe" },
{ role: "clusterMonitor", db: "admin" }
],
mechanisms: ["SCRAM-SHA-256"]
})

Роли (встроенные):

КатегорияРоли
Database Userread, readWrite
Database AdmindbAdmin, dbOwner, userAdmin
Cluster AdminclusterAdmin, clusterManager, clusterMonitor, hostManager
Backup/Restorebackup, restore
Superuserroot

Авторизация (RBAC):

  • Разрешения = роль + ресурс (db, collection, cluster).
  • Можно создавать кастомные роли:
    db.createRole({
    role: "devOps",
    privileges: [
    { resource: { db: "", collection: "" }, actions: ["listDatabases"] },
    { resource: { cluster: true }, actions: ["serverStatus"] }
    ],
    roles: []
    })

Шифрование:

  • TLS/SSL — для трафика (настраивается в mongod.conf: net.ssl.mode: requireSSL).
  • Encryption at Rest (Enterprise) — прозрачное шифрование данных на диске (WiredTiger).
  • Client-Side Field Level Encryption (CSFLE) — шифрование полей на клиенте (библиотеки: mongocryptd, key vault).
    Поддерживаемые алгоритмы: AEAD_AES_256_CBC_HMAC_SHA_512-Random, AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic.

Аудит (Enterprise):

  • Логирование операций: createUser, dropDatabase, authCheck, update, remove.
  • Конфигурация в mongod.conf:
    auditLog:
    destination: file
    format: JSON
    path: /var/log/mongodb/audit.log
    filter: '{ atype: { $in: ["createUser", "dropDatabase"] } }'

13. Настройка сервера (mongod.conf)

Основные секции конфигурационного файла (YAML):

storage:
dbPath: /var/lib/mongodb
journal:
enabled: true
wiredTiger:
engineConfig:
cacheSizeGB: 8.0 # ~50% RAM
collectionConfig:
blockCompressor: zlib # snappy (default), zlib, zstd
indexConfig:
prefixCompression: true

systemLog:
destination: file
path: /var/log/mongodb/mongod.log
logAppend: true
verbosity: 0 # 0-5, 1 — debug

net:
port: 27017
bindIp: 127.0.0.1,192.168.1.10
tls:
mode: requireTLS
certificateKeyFile: /etc/ssl/mongo.pem
CAFile: /etc/ssl/ca.pem

security:
authorization: enabled
keyFile: /etc/mongodb-keyfile # для replica set

replication:
replSetName: rs0
oplogSizeMB: 2048 # по умолчанию ~5% диска, минимум 990 МБ

sharding:
clusterRole: shardsvr # или configsvr, mongos

Важные параметры:

  • wiredTiger.cacheSizeGB — кэш данных/индексов (не должен превышать 50% RAM).
  • oplogSizeMB — размер oplog (журнала репликации); должен покрывать окно репликации (например, 24 ч при высокой нагрузке).
  • setParameter — динамические настройки:
    setParameter:
    transactionLifetimeLimitSeconds: 120
    wiredTigerConcurrentReadTransactions: 256
    wiredTigerConcurrentWriteTransactions: 128

14. Инструменты администрирования

ИнструментНазначениеКоманды/Особенности
mongoshShell (заменяет mongo)Поддержка await, TypeScript-подобный синтаксис, автодополнение.
mongodump / mongorestoreРезервное копированиеmongodump --uri="mongodb://host/db" --gzip --archive=backup.gz
mongoexport / mongoimportЭкспорт/импорт JSON/CSVНе для полных бэкапов (не сохраняет индексы, типы BSON).
mongostatМониторинг в реальном времениinsert, query, update, delete, getmore, command, flushes, mapped, vsize, res.
mongotopТоп операций по времениПоказывает read, write время на коллекцию.
db.currentOp()Текущие операцииФильтрация по secs_running, op, ns.
db.killOp(opid)Прерывание операцииОсторожно: может нарушить согласованность.
ProfilerЛог медленных запросовdb.setProfilingLevel(1, { slowms: 100 }), db.system.profile.find()
explain()Анализ плана запроса{ "queryPlanner", "executionStats", "allPlansExecution" }

Профилирование:

// Включить профилирование уровня 1 (только медленные)
db.setProfilingLevel(1, { slowms: 50 })

// Посмотреть лог
db.system.profile.find().sort({ ts: -1 }).limit(5)

// Сбросить
db.system.profile.drop()

Explain (пример):

db.users.find({ status: "active" }).sort({ createdAt: -1 }).explain("executionStats")

Ключевые поля в выводе:

  • executionStages.stageCOLLSCAN, IXSCAN, FETCH, SORT.
  • executionStats.totalDocsExamined — сколько документов прочитано.
  • executionStats.totalKeysExamined — сколько записей индекса прочитано.
  • executionStats.executionTimeMillis — время выполнения.

Идеальный запрос:
totalKeysExamined ≈ totalDocsExamined ≈ nReturned,
stageIXSCAN, без SORT или FETCH при покрывающем индексе.


15. Резервное копирование и восстановление

15.1. Типы бэкапов

ТипИнструментПреимуществаНедостатки
Logical (BSON/JSON)mongodump / mongorestoreПортативность, выбор БД/коллекций, совместимость версийМедленно при больших данных, не атомарно, не сохраняет индексы в точности (пересоздаются)
Physical (файловый)cp / rsync + fsyncLockОчень быстро, атомарно (если lock), сохраняет всё состояниеТребует остановки записи, зависит от storage engine, не переносим между версиями/ОС
Cloud (Atlas, Ops Manager)Atlas Backup / Ops ManagerАвтоматизация, point-in-time recovery, инкрементальные бэкапыТребует подписки, vendor lock-in
Oplog-basedmongodump --oplog + mongorestore --oplogReplayВозможность восстановления на конкретный момент (в пределах oplog)Только для replica set, требует достаточного размера oplog

15.2. mongodump — параметры и практики

mongodump \
--uri="mongodb://user:pass@host:27017/admin?authSource=admin" \
--db=universe \
--collection=posts \
--query='{"createdAt": {"$gte": {"$date": "2025-01-01T00:00:00Z"}}}' \
--gzip \
--archive=/backup/posts_2025.gz \
--oplog # только для replica set primary

Важные флаги:

  • --gzip — сжатие (экономит место и трафик).
  • --archive — один файл вместо директории (удобно для передачи).
  • --oplog — захватывает oplog в момент дампа (для point-in-time recovery).
  • --numParallelCollections=N — параллельная выгрузка коллекций (ускоряет).
  • --readPreference=secondary — снимать с secondary (разгрузка primary).

⚠️ Ограничения:

  • mongodump не сохраняет:
    • пользователей и роли (только данные),
    • функции system.js,
    • индексы с опцией partialFilterExpression (восстанавливаются без фильтра в старых версиях),
    • TTL-индексы (пересоздаются, но expireAfterSeconds сохраняется).
  • Для пользователей: бэкапить отдельно: mongodump --db=admin --collection=system.users.

15.3. mongorestore — восстановление

mongorestore \
--uri="mongodb://localhost:27017" \
--gzip \
--archive=/backup/posts_2025.gz \
--oplogReplay \ # применить oplog (если был --oplog)
--oplogLimit=1700000000:123 \ # timestamp:term
--drop \ # удалить коллекции перед восстановлением
--nsFrom="old.*" --nsTo="new.*" # переименование пространств имён

Рекомендации:

  • Всегда проверяйте целостность через --dryRun (но он не гарантирует полную проверку).
  • При восстановлении в production — делайте в maintenance window, отключите приложение.
  • После восстановления — пересоздайте индексы (db.coll.createIndex(...)) и проверьте db.coll.stats().

15.4. Файловые бэкапы (WiredTiger)

Только для standalone или secondary (с остановкой):

# На secondary (предварительно сделать hidden + отключить от репликации)
db.fsyncLock() # блокирует все записи
rs.freeze(10000) # заморозить на 10 000 сек (защита от выборов)

# В другом терминале:
sudo rsync -av /var/lib/mongodb/ /backup/mongodb_$(date +%F)/

# Вернуть в строй:
db.fsyncUnlock()
rs.stepDown()

⚠️ Не применяется к шардированным кластерам — только через mongodump или Ops Manager.

15.5. Point-in-Time Recovery (PITR)

Требования:

  • Replica set.
  • Достаточный размер oplog (должен покрывать окно бэкапа + время восстановления).
  • Бэкап через mongodump --oplog.

Процедура:

  1. Сделать mongodump --oplog.
  2. Записать optime из oplog-файла:
    bsondump dump/oplog.bson | tail -1  # → "ts": { "$timestamp": { "t": 1700000000, "i": 123 } }
  3. Восстановить дамп: mongorestore --oplogReplay --oplogLimit=1700000000:123.

16. Обновление MongoDB

Стратегии:

  • Rolling Upgrade — для replica set: secondary → secondary → primary (stepDown).
  • Major Version Upgrade — только последовательно: 5.0 → 6.0 → 7.0 (пропуск версий запрещён).
  • Feature Compatibility Version (FCV) — контролирует доступность новых функций.

Процедура (replica set, 6.0 → 7.0):

  1. Проверить совместимость:

    db.adminCommand({ getParameter: 1, featureCompatibilityVersion: 1 })
    // → { "version": "6.0" }
  2. Обновить все secondary (по одному):

    • Остановить mongod.
    • Заменить бинарники на 7.0.
    • Запустить.
    • Дождаться синхронизации (rs.status()stateStr: "SECONDARY").
  3. Обновить primary:

    rs.stepDown(60)  // уступить
    // затем обновить binary, как secondary
  4. Обновить FCV:

    db.adminCommand({ setFeatureCompatibilityVersion: "7.0" })

⚠️ Перед обновлением:

  • Протестировать на staging.
  • Проверить deprecated features (например, aggregate без курсора в 5.0+).
  • Убедиться, что драйверы совместимы (например, Node.js driver 5.x+ для MongoDB 6.0+).

17. Мониторинг и диагностика

17.1. Встроенные метрики

ИсточникКомандаОписание
serverStatusdb.runCommand({ serverStatus: 1 })Общее состояние: connections, mem, ops, asserts, extra_info (page faults), wiredTiger stats.
replSetGetStatusrs.status()Состояние replica set: lag, state, optime.
currentOpdb.currentOp({ secs_running: { $gt: 5 } })Долгие операции.
connectionsdb.serverStatus().connectionscurrent, available, totalCreated.
opcountersdb.serverStatus().opcountersinsert, query, update, delete, getmore, command.
wiredTiger cachedb.serverStatus().wiredTiger.cachebytes currently in the cache, tracked dirty bytes in the cache, maximum bytes configured.

17.2. Ключевые метрики для алертинга (Prometheus + mongodb_exporter)

МетрикаПорог (пример)Описание
mongodb_connections_current> 80% от connections.availableИсчерпание соединений
mongodb_replset_member_optime_lag_seconds> 30 сОтставание secondary
mongodb_wt_cache_bytes_dirty> 5% от cache.max_bytesГрязные страницы (риск flush)
mongodb_op_counters_repl_commandsрезкий ростАномальная активность
mongodb_asserts_regular> 0Ошибки приложения (например, duplicate key)
mongodb_extra_info_page_faultsрост на 100+/сНехватка RAM, активный swap

17.3. Профилирование и explain

Профиль медленных запросов:

// Включить
db.setProfilingLevel(1, { slowms: 50 })

// Посмотреть топ-10 медленных
db.system.profile.aggregate([
{ $match: { op: "query", millis: { $gt: 50 } } },
{ $sort: { millis: -1 } },
{ $limit: 10 },
{ $project: { ns: 1, millis: 1, query: 1, ts: 1 } }
])

Explain для агрегации:

db.orders.aggregate([
{ $match: { status: "shipped" } },
{ $group: { _id: "$region", total: { $sum: "$amount" } } }
]).explain("executionStats")

Ключевые индикаторы проблем:

  • executionStats.nReturned << executionStats.totalDocsExamined — неэффективный фильтр.
  • executionStages.stage: "SORT" после IXSCAN — отсутствует составной индекс.
  • executionStages.inputStage.stage: "COLLSCAN" — полное сканирование.

18. Миграция данных

18.1. Из другой СУБД (PostgreSQL → MongoDB)

Этапы:

  1. Анализ схемы: нормализованные таблицы → денормализованные документы.
  2. Проектирование документной модели (вложенные объекты vs. ссылки).
  3. ETL-скрипт (Python + PyMongo + psycopg2):
from pymongo import MongoClient
import psycopg2

pg = psycopg2.connect("...")
mongo = MongoClient("...")

with pg.cursor() as cur:
cur.execute("SELECT id, name, email FROM users")
for row in cur:
user = {
"_id": row[0],
"name": row[1],
"email": row[2],
"orders": [] # будет заполнено далее
}
mongo.universe.users.insert_one(user)

# Затем orders + join в памяти или bulk_write
  1. Валидация: подсчёт строк, контрольные суммы.

18.2. Миграция внутри MongoDB (изменение структуры)

Пример: вынос address в отдельный документ

// 1. Добавить новое поле (без downtime)
db.users.updateMany(
{ address: { $exists: true } },
[
{ $set: { addressId: { $toString: "$_id" } } }
],
{ multi: true }
)

// 2. Создать коллекцию addresses
db.users.aggregate([
{ $match: { address: { $exists: true } } },
{ $project: {
_id: "$addressId",
userId: "$_id",
street: "$address.street",
city: "$address.city"
} },
{ $out: "addresses" }
])

// 3. Удалить address из users (постепенно, пачками)
db.users.updateMany(
{ address: { $exists: true } },
{ $unset: { address: "" } },
{ limit: 1000 }
)

⚠️ Для production: использовать bulkWrite с паузами, логировать прогресс.


19. Best Practices (проверенные в production)

19.1. Схема и моделирование

  • Денормализация — норма, но не злоупотреблять (избыточность → согласованность).
  • Максимальный размер документа — 16 МБ; если близко — пересмотреть модель (разделить на поддокументы/коллекции).
  • Избегать массивов неограниченного роста (комментарии, логи) → выносить в отдельную коллекцию.
  • Использовать _id разумно:
    • ObjectId — по умолчанию,
    • UUID — при интеграции с внешними системами,
    • составные _id — для уникальности по нескольким полям (например, { userId: ..., ts: ... }).

19.2. Индексы

  • Правило EON для составных индексов: Equality → Sort → Range.
  • Покрывающие индексы (indexOnly: true в explain) — минимум обращений к документам.
  • Partial indexes для фильтров по статусу:
    db.orders.createIndex(
    { userId: 1, createdAt: -1 },
    { partialFilterExpression: { status: "active" } }
    )
  • Избегать индексов на высококардинальных полях без фильтрации (например, timestamp без status).

19.3. Производительность

  • Использовать projection — не выгружать 10 КБ документа ради 2 полей.
  • Постраничное разбиение через ключ, а не skip:
    // Плохо:
    db.users.find().sort({ _id: 1 }).skip(10000).limit(10)
    // Хорошо:
    db.users.find({ _id: { $gt: lastSeenId } }).sort({ _id: 1 }).limit(10)
  • Bulk-операции вместо поштучных:
    db.users.bulkWrite([
    { updateOne: { filter: { _id: 1 }, update: { $inc: { views: 1 } } } },
    { updateOne: { filter: { _id: 2 }, update: { $inc: { views: 1 } } } }
    ])

19.4. Надёжность

  • Replica set из 3+ узлов в разных зонах доступности.
  • Oplog size ≥ 24 часа писковой нагрузки.
  • Backups ежедневно + PITR для критичных БД.
  • Тестирование восстановления — ежеквартально.

19.5. Безопасность

  • TLS everywhere — между клиентом, mongos, шардами, config servers.
  • RBAC — минимальные привилегии, отдельные учётные записи для приложений.
  • CSFLE — для PII (персональных данных): email, phone, passport.
  • Аудит — логировать dropDatabase, createUser, grantRoles.